
function callRenderMachineTape() {
    renderer.renderMachineTape(currentMachine.getTape(), currentMachine.getCursor());
};

function callRenderFinalStateTape() {
    renderer.renderFinalStateTable(currentTask.getOutputTape());
};

function createMachineForCurrentTask() {
    var tape = currentTask.getInputTape().copy();
    var haltState = new State("HALT");
    var table = new RulesTable(new Array(haltState));
    currentMachine = new TuringMachine(tape, table, currentTask.getInitialCursorPosition());
};

function getMachineStatesFromDOM() {
    var states = new Array();
    var initState;
    $(".state_name").each(function () {
        var name = $(this).attr("name");
        states[name] = new State(name);
        if (!initState) {
            initState = states[name];
        }
    });
    states["HALT"] = new State("HALT");
    var symbols = currentMachine.getAlphabet().getSymbols();
    for (var stateName in states) {
        var state = states[stateName];
        if (stateName === "HALT") {
            continue;
        }
        var rules = new Array();
        for (var i in symbols) {
            var symbol = symbols[i];
            var tdId = "state_" + state.getName() + "_symbol_" + symbol;
            var moveSelectedOption = $("#" + tdId + " .move_select option:selected");
            var symbolSelectedOption = $("#" + tdId + " .symbol_select option:selected");
            var stateSelectedOption = $("#" + tdId + " .state_select option:selected");
            var rule = new Rule(
                moveSelectedOption.attr("value") === "left" ? Rule.MoveType.LEFT : Rule.MoveType.RIGHT,
                symbolSelectedOption.attr("value"),
                states[stateSelectedOption.attr("value")].getName()
            );
            rules.push(new Array(symbol, rule));
        }
        currentMachine.addState(state.getName(), currentMachine.getAlphabet(), rules);
    }
};

function machineMakeStep() {
    if (!machine_running) {
        getMachineStatesFromDOM();
        machine_running = true;
        var initState;
        $(".state_name").each(function () {
            var name = $(this).attr("name");
            if (!initState)
                initState = currentMachine.getStates()[name];
        });
        currentMachine.setCurrentState(initState);
    }

    var halted = !currentMachine.nextStep();

    if (halted) {
        machineStop(false);

        var solutionIsCorrect = currentTask.isCorrectSolution(currentMachine.getTape(), currentMachine.getCursor());
        if (solutionIsCorrect) {
            alert("Well done! You've beaten the level.");
        } else {
            alert("The machine halted with wrong answer. You suck!");
        }
    } else {
        callRenderMachineTape();
    }

    return halted;
};

function machineRun() {
    timer = $.timer(machineMakeStep, 500, true);
};

function machineStop(reset) {
    if (typeof (timer) !== "undefined") {
        timer.stop();
    }

    if (reset) {
        currentMachine.reset(currentTask.getInputTape().copy(), currentTask.getInitialCursorPosition());
        machine_running = false;
        callRenderMachineTape();
    }

    renderer.enableRulesTableControls(true);
    renderer.enableControl(Renderer.ids.START_ID, true);
    renderer.enableControl(Renderer.ids.STEP_ID, true);
};

function setTask(task) {
    currentTask = task;
    createMachineForCurrentTask();
    machine_running = false;

    renderer.printTaskName(currentTask);
    callRenderFinalStateTape();
    renderer.renderRulesTable(currentMachine);
    callRenderMachineTape();

    renderer.enableRulesTableControls(true);
    renderer.enableControl(Renderer.ids.START_ID, true);
    renderer.enableControl(Renderer.ids.STEP_ID, true)
};

function onTaskSelectChangeEvent() {
    if (typeof(machine_running) !== "undefined" && machine_running) {
        machineStop();
    }

    var tasks = getAllTasks();
    var index = $(Renderer.ids.TASK_SELECT_ID).val();
    setTask(tasks[index]);
};

function onAddStateButtonClickEvent() {
    var s = prompt("state name").trim();
    var regEx = /^\w+$/i;
    if (s && !currentMachine.hasState(s) && regEx.test(s)) {
        currentMachine.addState(s, currentMachine.getAlphabet());
        getMachineStatesFromDOM();
        renderer.renderRulesTable(currentMachine);
    } else {
        alert("invalid name");
    }
};

function onStepButtonClickEvent() {
    renderer.enableRulesTableControls(false);

    machineMakeStep();
};

function onStartButtonClickEvent() {
    renderer.enableRulesTableControls(false);
    renderer.enableControl(Renderer.ids.START_ID, false);
    renderer.enableControl(Renderer.ids.STEP_ID, false);

    machineRun();
};

function onResetButtonClickEvent() {
    machineStop(true);
};

function onHelpButtonClickEvent() {
    $(Renderer.ids.HELP_DIALOG).dialog();
};

function onDocumentReadyEvent() {
    $.ajaxSetup({async:false});

    renderer = new Renderer();

    $(Renderer.ids.TASK_SELECT_ID).change(onTaskSelectChangeEvent);
    $(Renderer.ids.STATE_ADD_ID).click(onAddStateButtonClickEvent);
    $(Renderer.ids.STEP_ID).click(onStepButtonClickEvent);
    $(Renderer.ids.START_ID).click(onStartButtonClickEvent);
    $(Renderer.ids.RESET_ID).click(onResetButtonClickEvent);
    $(Renderer.ids.HELP_ID).click(onHelpButtonClickEvent);

    renderer.loadTaskNames();
    onTaskSelectChangeEvent();
};

$(document).ready(onDocumentReadyEvent);
